#include <everest/dbc/odbc/odbc_common.h>

#include <everest/dbc/odbc/odbc_common.h>
#include <vector>
#include <string.h>

using namespace everest::dbc::odbc;
using namespace everest::dbc;
using namespace std;

OdbcConnection::OdbcConnection(SQLHANDLE hConn)
: m_ConHandle(hConn)
{
	// EMPTY
}

OdbcConnection::~OdbcConnection() throw()
{
	try {
		this->close();
	} catch (...) {
		// EMPTY
	}

	return ;
}

void OdbcConnection::release()
{
	delete this;
}

bool OdbcConnection::close()
{
	OdbcException e;
	if ( m_ConHandle != SQL_NULL_HANDLE )
	{
		SQLRETURN ret = SQLDisconnect(m_ConHandle);
		if ( ret == SQL_ERROR ) e.set(SQL_HANDLE_DBC, m_ConHandle, "OdbcConnection::close()");
		SQLFreeHandle(SQL_HANDLE_DBC, m_ConHandle);
	}
	m_ConHandle = SQL_NULL_HANDLE;

	if ( e.code() != 0 ) throw e;
	else return true;
}

void OdbcConnection::setAutoCommit(bool bAutoCommit)
{
	if ( m_ConHandle == SQL_NULL_HANDLE ) {
		throw OdbcException::InvalidHandle(SQL_HANDLE_DBC, m_ConHandle, "OdbcConnection::setAutoCommit()");
	}

	SQLPOINTER val((SQLPOINTER)(bAutoCommit?SQL_AUTOCOMMIT_ON:SQL_AUTOCOMMIT_OFF));
	SQLRETURN ret = SQLSetConnectAttr(m_ConHandle, SQL_ATTR_AUTOCOMMIT, val, 0);
	if ( ret == SQL_ERROR )
		throw OdbcException(SQL_HANDLE_DBC, m_ConHandle, "OdbcConnection::setAutoCommit()");
	return ;
}

bool OdbcConnection::getAutoCommit() const
{
	if ( m_ConHandle == SQL_NULL_HANDLE ) {
		throw OdbcException::InvalidHandle(SQL_HANDLE_DBC, m_ConHandle, "OdbcConnection::getAutoCommit()");
	}

	SQLUINTEGER val;
	SQLRETURN ret = SQLGetConnectAttr(m_ConHandle, SQL_ATTR_AUTOCOMMIT, &val, SQL_IS_UINTEGER, NULL);
	if ( ret == SQL_ERROR )
		throw OdbcException(SQL_HANDLE_DBC, m_ConHandle, "OdbcConnection::setAutoCommit()");
	return val == SQL_AUTOCOMMIT_OFF?false:true;
}

void OdbcConnection::commit()
{
	if ( m_ConHandle == SQL_NULL_HANDLE ) {
		throw OdbcException::InvalidHandle(SQL_HANDLE_DBC, m_ConHandle, "OdbcConnection::commit()");
	}

	SQLRETURN ret = SQLEndTran(SQL_HANDLE_DBC, m_ConHandle, SQL_COMMIT);
	if ( ret == SQL_ERROR )
		throw OdbcException(SQL_HANDLE_DBC, m_ConHandle, "OdbcConnection::commit()");
	return ;
}

void OdbcConnection::rollback()
{
	if ( m_ConHandle == SQL_NULL_HANDLE) {
		throw OdbcException::InvalidHandle(SQL_HANDLE_DBC, m_ConHandle, "OdbcConnection::rollback()");
	}

	SQLRETURN ret = SQLEndTran(SQL_HANDLE_DBC, m_ConHandle, SQL_ROLLBACK);
	if ( ret == SQL_ERROR )
		throw OdbcException(SQL_HANDLE_DBC, m_ConHandle, "OdbcConnection::rollback()");
	return ;
}

bool OdbcConnection::isClosed() const
{
	if ( m_ConHandle == SQL_NULL_HANDLE)
		return true;
	else
		return false;
}

IStatement * OdbcConnection::createStatement()
{
	SQLHANDLE hStm = SQL_NULL_HANDLE;
	SQLRETURN ret = SQLAllocHandle(SQL_HANDLE_STMT, m_ConHandle, &hStm);
	if ( ret == SQL_ERROR )
		throw OdbcException(SQL_HANDLE_DBC, m_ConHandle, "OdbcConnection::createStatement()");
	else if ( ret == SQL_INVALID_HANDLE )
		throw OdbcException::InvalidHandle(SQL_HANDLE_DBC, m_ConHandle, "OdbcConnection::createStatement()");

	return new OdbcStatement(hStm, this);
}


/**
 * Create a prepared statement with specified SQL statement string.
 * @param pstrSql  sql string
 * @return pointer to the created prepared statement.
 */
IPreparedStatement * OdbcConnection::prepareStatement(const char *pstrSql)
{
	SQLHANDLE hStm = SQL_NULL_HANDLE;
	SQLRETURN ret = SQLAllocHandle(SQL_HANDLE_STMT, m_ConHandle, &hStm);
	if ( ret == SQL_ERROR )
		throw OdbcException(SQL_HANDLE_DBC, m_ConHandle, "OdbcConnection::prepareStatement(){1}");
	else if ( ret == SQL_INVALID_HANDLE )
		throw OdbcException::InvalidHandle(SQL_HANDLE_DBC, m_ConHandle, "OdbcConnection::prepareStatement(){1}");

	ret = SQLPrepare(hStm, (SQLCHAR *)pstrSql, SQL_NTS);
	if ( ret == SQL_ERROR )
		throw OdbcException(SQL_HANDLE_DBC, m_ConHandle, "OdbcConnection::prepareStatement(){2}");
	else if ( ret == SQL_INVALID_HANDLE )
		throw OdbcException::InvalidHandle(SQL_HANDLE_DBC, m_ConHandle, "OdbcConnection::prepareStatement(){2}");

	return new OdbcPreparedStatement(hStm, this);
}


